/*
 * Copyright © 2004-2014 chenYuan. All rights reserved.
 * @Website:wwww.jspx.net
 * @Mail:39793751@qq.com
 * @author: chenYuan , 陈原
 * @License: Jspx.net Framework Code is open source (LGPL)，Jspx.net Framework 使用LGPL 开源授权协议发布。
 * @jvm:jdk1.6+  x86/amd64
 *
 */
package com.jspx.sioc.factory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jspx.scriptmark.core.TagNode;
import com.jspx.sioc.BeanFactory;
import com.jspx.sioc.IocContext;
import com.jspx.sioc.Sioc;
import com.jspx.sioc.annotation.Ref;
import com.jspx.sioc.tag.ArrayElement;
import com.jspx.sioc.tag.BeanElement;
import com.jspx.sioc.tag.ListElement;
import com.jspx.sioc.tag.MapElement;
import com.jspx.sioc.util.TypeUtil;
import com.jspx.utils.ArrayUtil;
import com.jspx.utils.BeanUtil;
import com.jspx.utils.ClassUtil;
import com.jspx.utils.StringUtil;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User:chenYuan (mail:39793751@qq.com)
 * Date: 2007-2-12
 * Time: 15:59:47
 * ioc 对象工厂
 */
public final class EntryFactory implements BeanFactory {
    private final static Logger log = LoggerFactory.getLogger(EntryFactory.class);
    private final static Lifecycle lifecycle = new LifecycleManager();
    private static IocContext iocContext;

    public IocContext getIocContext() {
        return iocContext;
    }

    public void setIocContext(IocContext iocContext) {
        EntryFactory.iocContext = iocContext;
    }

    /**
     * 创建生命周期对象
     *
     * @param beanElement 配置
     * @param namespace   命名空间
     * @return 返回生命对象
     * @throws Exception 错误
     *                   解析错误
     */
    private LifecycleObject createLifecycleObject(BeanElement beanElement, final String namespace) throws Exception {
        Map<String, Object> paraMap = Collections.synchronizedMap(TypeUtil.getPropertyValue(beanElement.getPropertyElements(), namespace));
        //////////////array begin
        for (TagNode element : beanElement.getArrayElements()) {
            ArrayElement array = (ArrayElement) element;
            paraMap.put(array.getId(), TypeUtil.getArrayValue(array, namespace, this));
        }
        //////////////array end

        //////////////list begin
        for (TagNode element : beanElement.getListElements()) {
            ListElement list = (ListElement) element;
            paraMap.put(list.getId(), TypeUtil.getListValue(list, namespace, this));
        }
        //////////////list end

        //////////////map begin
        for (TagNode element : beanElement.getMapElements()) {
            MapElement map = (MapElement) element;
            paraMap.put(map.getId(), TypeUtil.getMapValue(map, namespace, this));
        }
        //////////////map end

        LifecycleObject lifecycleObject = new LifecycleObject();
        lifecycleObject.setSingleton(beanElement.isSingleton());
        lifecycleObject.setClassName(beanElement.getClassName());
        lifecycleObject.setInitMethod(beanElement.getInitMethod());
        lifecycleObject.setDestroyMethod(beanElement.getDestroyMethod());
        lifecycleObject.setCreate(beanElement.getCreate());
        lifecycleObject.setNamespace(namespace);
        lifecycleObject.setParamMap(paraMap);
        if (lifecycleObject.isSingleton()) {
            //创建最底层的对象
            BeanElement beanElementTmp = iocContext.getBeanElement(lifecycleObject.getName(), lifecycleObject.getNamespace());
            if (beanElementTmp != null) lifecycleObject.setNamespace(beanElementTmp.getNamespace());
            lifecycleObject.setObject(createEntry(lifecycleObject));
        }
        return lifecycleObject;
    }

    /**
     * @param lifecycleObject 创建实体对象
     * @return 创建实体对象
     * @throws Exception
     */
    public Object createEntry(LifecycleObject lifecycleObject) throws Exception {
        if (lifecycleObject.isSingleton() && lifecycleObject.getObject() != null) return lifecycleObject.getObject();
        Object result = null;
        try {
            result = ClassUtil.newInstance(lifecycleObject.getClassName());
        } catch (Exception e) {
            log.error("error create Entry:" + lifecycleObject.getClassName() + "@" + lifecycleObject.getNamespace() + " 不能够实例化,检查配置", e);
        }
        //////////////设置参数 begin
        Map<String, Object> paramMap = lifecycleObject.getParamMap();
        for (String name : paramMap.keySet()) {
            if (name == null) continue;
            Object pValue = paramMap.get(name);
            if (pValue instanceof String) {
                String tmp = (String) pValue;
                if (tmp.startsWith(Sioc.IocLoad)) {
                    tmp = tmp.substring(Sioc.IocLoad.length());
                    String beanName = tmp.substring(0, tmp.indexOf(Sioc.IocFen));
                    pValue = this.getBean(beanName, lifecycleObject.getNamespace());
                } else if (tmp.contains(Sioc.IocRootNamespace)) {
                    pValue = StringUtil.replace((String) pValue, Sioc.IocRootNamespace, TypeUtil.getRootNamespace(lifecycleObject.getNamespace()));
                } else if (tmp.equals(Sioc.IocNamespace)) {
                    pValue = StringUtil.replace((String) pValue, Sioc.IocNamespace, TypeUtil.getRootNamespace(lifecycleObject.getNamespace()));
                }
            }
            BeanUtil.setSimpleProperty(result, name, pValue);
        }

        ////////代理创建对象 begin
        if (!StringUtil.isNULL(lifecycleObject.getCreate())) {
            result = BeanUtil.getProperty(result, lifecycleObject.getCreate());
        }
        ////////代理创建对象 end

        ////////Ref对象注入 begin



        setRef(result, lifecycleObject.getRefNamespace());
        ////////Ref对象注入 end

        if (!StringUtil.isNULL(lifecycleObject.getInitMethod())) {
            BeanUtil.invoke(result, lifecycleObject.getInitMethod());
        }
        return result;
    }

    /**
     * 设置资源后的对象
     *
     * @param o         对象
     * @param namespace 命名空间
     * @throws Exception
     */
    public void setRef(Object o, final String namespace) throws Exception {
        if (o == null) return;

        //字段
        Method[] methods = ClassUtil.getDeclaredSetMethods(o.getClass());
        if (methods != null)
            for (Method method : methods) {
                Ref ref = method.getAnnotation(Ref.class);
                if (ref != null) {
                    String m_namespace = StringUtil.isNULL(ref.namespace()) || Sioc.global.equalsIgnoreCase(ref.namespace()) ? namespace : ref.namespace();
                    if (ref.test()) {

                        if (!containsBean(ref.name(), m_namespace))
                            log.info("sioc config error, no find name=" + ref.name() + " namespace=" + m_namespace + " ref=" + o.getClass().getName());
                        else {

                            method.invoke(o, getBean(ref.name(), m_namespace));
                        }
                    } else if (!ref.test()) {
                        method.invoke(o, getBean(ref.name(), m_namespace));
                    }
                }
            }
    }

    /**
     * @param beanName bean id
     * @return 得到bean对象
     */
    public Object getBean(String beanName) {
        String namespace = null;
        if (beanName.contains("@")) {
            namespace = StringUtil.substringAfter(beanName, "@");
            beanName = StringUtil.substringBefore(beanName, "@");
        }
        if (StringUtil.isNULL(namespace)) namespace = Sioc.global;
        try {
            return getBean(beanName, namespace);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param beanName  bean id
     * @param namespace 命名空间
     * @return 得到bean对象
     */
    final synchronized public Object getBean(String beanName, final String namespace) {
        if (!StringUtil.hasLength(beanName)) return null;
        String nameK = StringUtil.isNULL(namespace) ? Sioc.global : namespace;
        if (beanName.contains("@")) {
            beanName = StringUtil.substringBefore(beanName, "@");
            nameK = StringUtil.substringAfterLast(beanName, "@");
        }
        LifecycleObject lifecycleObject = null;
        try {
            lifecycleObject = getLifecycleObject(beanName, namespace);
            if (lifecycleObject != null && lifecycleObject.isSingleton()) {
                return lifecycleObject.getObject();
            }
        } catch (Exception e) {
            log.error("create object error " + beanName + "@" + namespace, e);
        }

        try {
            //得到的所在命名空间 begin
            BeanElement beanElement = iocContext.getBeanElement(beanName, nameK);
            if (beanElement == null) {
                Exception e = new Exception("不能发现配置:sioc no find bean BeanElement name:" + beanName + " namespace:" + namespace);
                log.error("不能发现配置:sioc no find bean BeanElement name:" + beanName + " namespace:" + namespace, e);
                throw e;
            }
            //得到的所在命名空间 end

            //得到所在空间对象 不使用synchronized多线程,单列在高压下不成功
            //判断是否有继承关系的单例对象
            lifecycleObject = createLifecycleObject(beanElement, beanElement.getNamespace());
            if (!beanElement.getNamespace().equalsIgnoreCase(namespace) && Sioc.global.equalsIgnoreCase(beanElement.getNamespace()))
                lifecycleObject.setRefNamespace(nameK);

            lifecycle.put(beanName, lifecycleObject);
            //如果没有就创建
        } catch (Exception e) {
            log.error("bean name:" + beanName + "  namespace:" + namespace, e);
            e.printStackTrace();
        }
        if (lifecycleObject == null) {
            log.warn("not find bean name:" + beanName + "  namespace:" + namespace);
            return null;
        }
        if (lifecycleObject.isSingleton()) {
            return lifecycleObject.getObject();
        } else {
            try {
                return createEntry(lifecycleObject);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    /**
     * @param beanName  bean id
     * @param namespace 命名空间
     * @return 按照继承关系，找到最底层的配置对象
     * @throws Exception
     */
    public LifecycleObject getLifecycleObject(final String beanName, final String namespace) throws Exception {
        String nameK = namespace;
        LifecycleObject lifecycleObject;

        final Map<String, String> extendMap = iocContext.getExtendMap();
        while (!StringUtil.isNULL(nameK) && !Sioc.global.equalsIgnoreCase(nameK)) {
            lifecycleObject = lifecycle.get(beanName, nameK);
            if (lifecycleObject != null) return lifecycleObject;

            //判断是否有继承关系的单例对象 注意sync
            BeanElement beanElement = iocContext.getBeanElementConfig(beanName, nameK);
            if (beanElement != null && beanElement.getNamespace().equalsIgnoreCase(nameK)) {
                lifecycleObject = createLifecycleObject(beanElement, nameK);
                lifecycle.put(beanName, lifecycleObject);
                return lifecycleObject;
            }
            //如果没有就创建

            if (extendMap.containsKey(nameK))
                nameK = extendMap.get(nameK);
            else {
                if (nameK.contains("/")) nameK = StringUtil.substringBeforeLast(nameK, "/");
                else nameK = Sioc.global;
            }
        }

        // global 处理 begin
        lifecycleObject = lifecycle.get(beanName, Sioc.global);
        if (lifecycleObject != null) return lifecycleObject;
        BeanElement beanElement = iocContext.getBeanElementConfig(beanName, Sioc.global);
        if (beanElement != null && beanElement.getNamespace().equalsIgnoreCase(nameK)) {
            lifecycleObject = createLifecycleObject(beanElement, beanElement.getNamespace());
            lifecycle.put(beanName, lifecycleObject);
            return lifecycleObject;
        }
        return null;
    }

    /**
     * @param beanName        bean名称
     * @param lifecycleObject 生命空间
     * @return 单例注册
     */
    public boolean registerBeanSingleton(String beanName, LifecycleObject lifecycleObject) {
        if (beanName == null) return false;
        if (lifecycleObject != null) lifecycleObject.setSingleton(true);
        assert lifecycleObject != null;
        if (lifecycleObject.getObject() == null) {
            try {
                lifecycleObject.setObject(createEntry(lifecycleObject));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            lifecycle.put(beanName, lifecycleObject);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("IOC注册类对象发生错误", e);
        }
        return true;
    }

    /**
     * @param beanName bean id
     * @return 判断是否存在对象
     */
    public boolean containsBean(String beanName) {
        return containsBean(beanName, null);
    }

    /**
     * @param beanName  bean id
     * @param namespace 命名空间
     * @return 判断是否存在对象
     */
    public boolean containsBean(String beanName, final String namespace) {
        try {
            return iocContext.containsBean(beanName, namespace);
        } catch (Exception e) {
            log.debug(e.getLocalizedMessage());
        }
        return false;
    }

    /**
     * @param beanName  bean id
     * @param namespace 命名空间
     * @return 判断是否为单列
     * @throws Exception
     */
    public boolean isSingleton(String beanName, final String namespace) throws Exception {
        try {
            BeanElement beanElement = iocContext.getBeanElement(beanName, namespace);
            return beanElement != null && beanElement.isSingleton();
        } catch (Exception e) {
            log.debug(e.getLocalizedMessage());
        }
        return false;
    }

    /**
     * @param beanName  bean id
     * @param namespace 命名空间
     * @return 得到别名列表
     */
    public String[] getAliases(String beanName, final String namespace) {
        try {
            BeanElement beanElement = iocContext.getBeanElement(beanName, namespace);
            if (beanElement != null)
                return beanElement.getAliases();
        } catch (Exception e) {
            log.debug(e.getLocalizedMessage());
        }
        return ArrayUtil.emptyString;
    }

    /*
     * 得到sioc 中配置的所有应用
     * @return 目的为了方便判断当前开启应用的应用
     */
    public Map<String, String> getApplicationMap() {
        return iocContext.getApplicationMap();
    }

    public void shutdown() {
        lifecycle.shutdown();
    }
}